package org.kleathome.dcpu16;

import org.kleathome.dcpu16.emulator.Emulator;

public class DCPU16Address extends DCPU16Value
{
	protected char _address = 0x0000;
	protected Emulator.Registers _register;
	protected DCPU16Instruction _instruction = null;
	protected boolean useRegister = false;
	protected boolean useBoth = false;
	protected char _value = 0x0000;

	public DCPU16Address(char value)
	{
		_value = value;
		if (value >= 0x08 && value <= 0x0F)
		{
			useRegister = true;
			_register = Emulator.Registers.values()[value - 0x08];
		}
		else if (value >= 0x10 && value <= 0x17)
		{
			useBoth = true;
			_register = Emulator.Registers.values()[value - 0x10];
			_address = Emulator.nextInstruction();
		}
		else if (value == 0x18)
		{
			_instruction = new DCPU16Pop();
		}
		else if (value == 0x19)
		{
			_instruction = new DCPU16Peek();
		}
		else if (value == 0x1A)
		{
			_instruction = new DCPU16Push();
		}
		else if (value == 0x1E)
		{
			_address = Emulator.nextInstruction();
		}
		else
			_address = value;
	}
	
	public String toString()
	{
		if (_instruction != null)
			return _instruction.toString();
		else if (useRegister)
			return "Register [" + _register + "]";
		else if (useBoth)
			return "Address [0x" + Integer.toHexString(_address) + " + " + _register + "]";
		else
			return "Address [0x" + Integer.toHexString(_address) + "]";
	}

	@Override
	public char getValue()
	{
		if (_instruction != null)
		{
			_instruction.execute();
			if (_instruction.getOpCodeLiteral().toLowerCase().equals("pop"))
			{
				return Emulator.getMemory(Emulator.getStackPointer());
			}
			else if (_instruction.getOpCodeLiteral().toLowerCase().equals("peek"))
			{
				return Emulator.getMemory(Emulator.getStackPointer());
			}
			else if (_instruction.getOpCodeLiteral().toLowerCase().equals("push"))
			{
				System.err.println("Push not allowed as get");
				System.exit(3);
			}
			System.err.println("Instruction is not push/pop/peek");
			System.exit(3);
			return 0;
		}
		else if (useBoth)
		{
			return Emulator.getMemory((char)(_address + Emulator.getRegister(_register)));
		}
		else if (useRegister)
		{
			return Emulator.getRegister(_register);
		}
		else
		{
			return Emulator.getMemory(_address);
		}
	}

	@Override
	public void setValue(char in)
	{
		if (_instruction != null)
		{
			if (_instruction.getOpCodeLiteral().toLowerCase().equals("peek"))
			{
				System.err.println("Peek not allowed as set");
				System.exit(3);
			}
			else if (_instruction.getOpCodeLiteral().toLowerCase().equals("pop"))
			{
				System.err.println("Pop not allowed as set");
				System.exit(3);
			}
			else if (_instruction.getOpCodeLiteral().toLowerCase().equals("push"))
			{
				Emulator.setMemory(Emulator.getStackPointer(), in);
			}
			else
			{
				System.err.println("Instruction is not push/pop/peek");
				System.exit(3);
			}
			_instruction.execute();
		}
		else if (useBoth)
		{
			Emulator.setMemory((char)(_address + Emulator.getRegister(_register)), in);
		}
		else if (useRegister)
		{
			Emulator.setRegister(_register, in);
		}
		else
		{
			Emulator.setMemory(_address, in);
		}
	}

	@Override
	public int getCycleCost()
	{
		if (useBoth)
			return 1;
		if (_value == 0x1E)
			return 1;
		return 0;
	}
	
	@Override
	public int getInstructionWords()
	{
		if (_instruction != null)
			return 0;
		if (_value >= 0x10 && _value <= 0x17 || _value == 0x1E)
			return 1;
		return 0;
	}
	
	@Override
	public void updateJumpAddresses()
	{
	}

	@Override
	public int getOpCode()
	{
		return _value;
	}

	@Override
	public char getDataCode()
	{
		if (_value == 0x1E)
		{
			return _address;
		}
		else if (_value >= 0x10 && _value <= 0x17)
		{
			return _address;
		}
		return 0;
	}
}
